import { AnyClass, AnyFunction, AnyObject } from '../types'
import { isInterval, isMyRoom } from './common'
import { colorful } from './console'

/**
 * 方法装饰器工厂，自动将某函数注入到某对象的原型中
 */
export function injectPrototype(constructor: AnyObject) {
    return function <T extends AnyFunction>(target: any, propertyKey: string, descriptor: TypedPropertyDescriptor<T>) {
        const { value: fn } = descriptor
        if (typeof fn !== 'function') return
        try {
            Object.defineProperty(constructor.prototype, propertyKey, {
                value: function (...args: unknown[]) {
                    return fn.call(target, this, ...args)
                },
                configurable: descriptor.configurable,
                enumerable: descriptor.enumerable,
            })
        } catch (error) {
            //
        }
    }
}

/**
 * 注入Room原型
 */
export const injectRoomPrototype = injectPrototype(Room)

/**
 * 注入Creep原型
 */
export const injectCreepPrototype = injectPrototype(Creep)

/**
 * 类装饰器，阻止类被实例化
 */
export function preventConstructor(constructor: AnyClass) {
    throw new Error(`${constructor.name} 不可被实例化！`)
}

/**
 * 方法装饰器工厂，让方法每隔一定时间才会执行一次
 */
export function interval(time: number) {
    return function <T extends AnyFunction>(target: any, propertyKey: string, descriptor: TypedPropertyDescriptor<T>) {
        const { value: fn } = descriptor
        descriptor.value = function (...args: any[]) {
            if (!isInterval(time)) return
            return fn?.apply(this, args)
        } as T
    }
}

/**
 * 方法装饰器，拦截不是自己的房间
 */
export function interceptRoom(warn = '房间不属于你') {
    return function <T extends AnyFunction>(target: any, _: string, descriptor: TypedPropertyDescriptor<T>) {
        const { value: fn } = descriptor
        descriptor.value = function (roomOrName: Room | string, ...args: unknown[]) {
            const isString = typeof roomOrName === 'string'
            const room = isString ? Game.rooms[roomOrName] : roomOrName
            if (!isMyRoom(room)) {
                return colorful(`${isString ? `[${roomOrName}]` : roomOrName}: ${warn}`, 'red')
            }
            return fn?.call(target, roomOrName, ...args)
        } as T
    }
}
